package com.yql.spring;

import java.beans.Introspector;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Scanner;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * ClassName:ApplicationContext
 * Description:
 * IOC容器 AOP实现
 * @author Yql
 * @version 1.0
 * @Create 2024/9/1
 **/


public class ApplicationContext {


    private Class configClass;

    // BeanDefinition集合
    private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

    // 单例池
    private ConcurrentMap<String, Object> singletonObjects = new ConcurrentHashMap<>();

    // BeanPostProcessor 后置处理器链
    private ArrayList<BeanPostProcessor> beanPostProcessors = new ArrayList<>();

    public ApplicationContext(Class configClass) {
        this.configClass = configClass;

        // 当前配置类上 有@ComponentScan注解
        if (configClass.isAnnotationPresent(ComponentScan.class)) {
            ComponentScan componentScan = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            // 获取@Component中配置的扫描路径、替换分隔符、拼接classpath
            String path = componentScan.value();

            path = path.replace(".", "/");

            ClassLoader classLoader = ApplicationContext.class.getClassLoader();
            // getResource 会自动拼接 classpath，这个 URL 指向编译后的 service 这个目录：D:\GitHub\yi-spring\out\com\yql\service
            URL resource = classLoader.getResource(path);

            File file = new File(resource.getFile());

            // 扫描目标地址下的所有文件
            if (file.isDirectory()) {

                File[] files = file.listFiles();
                for (File f : files) {
                    String fileName = f.getAbsolutePath();

                    // 判断是不是.class文件
                    if (fileName.endsWith(".class")) {
                        // 拿到类的全限定名
                        String className = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class"));
                        className = className.replace("\\", ".");

                        try {
                            Class<?> clazz = classLoader.loadClass(className);

                            // 是否实现了后置处理器 BeanPostProcessor 接口
                            if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
                                BeanPostProcessor beanPostProcessor = (BeanPostProcessor) clazz.newInstance();
                                beanPostProcessors.add(beanPostProcessor);
                            }

                            // 判断是否有@Component注解，有则需要生成BeanDefinition
                            if (clazz.isAnnotationPresent(Component.class)) {


                                BeanDefinition beanDefinition = new BeanDefinition();
                                beanDefinition.setType(clazz);
                                // 设置作用域
                                if (clazz.isAnnotationPresent(Scope.class)) {
                                    beanDefinition.setScope(clazz.getAnnotation(Scope.class).value());
                                } else {
                                    beanDefinition.setScope("singleton");
                                }

                                //设置类名字
                                String beanName = clazz.getAnnotation(Component.class).name();
                                if ("".equals(beanName)) {
                                    beanName = Introspector.decapitalize(clazz.getSimpleName());
                                }
                                beanDefinitionMap.put(beanName, beanDefinition);
                            }


                        } catch (ClassNotFoundException e) {
                            throw new RuntimeException(e);
                        } catch (InstantiationException e) {
                            throw new RuntimeException(e);
                        } catch (IllegalAccessException e) {
                            throw new RuntimeException(e);
                        }
                    }

                }

            }

        }
        // 扫描完成，创建Bean（实例化）
        for (String beanName : beanDefinitionMap.keySet()){
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            // 单例则直接创建Bean，并放入单例池中
            if("singleton".equals(beanDefinition.getScope())){
                Object bean = createBean(beanName, beanDefinition);
                singletonObjects.put(beanName, bean);
            }
        }

    }



    public Object createBean(String beanName, BeanDefinition beanDefinition){

        Class clazz = beanDefinition.getType();
        Object instance = null;

        try{
            // 1. 实例化
            System.out.println("开始创建： " + beanName + " 对象(" + clazz.getName() + ")...." );
            instance = clazz.getConstructor().newInstance();

            // 2. 属性注入DI，处理@Autowire注解
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields){

                if (field.isAnnotationPresent(Autowired.class)){
                    field.setAccessible(true);
                    field.set(instance, getBean(field.getName()));
                }
            }

            // 3. Aware接口回调
            if (instance instanceof BeanNameAware){
                ((BeanNameAware) instance).setBeanName(beanName);
            }


            // 4. 初始化前
            // 遍历 BeanPostProcessor 列表，执行 postProcessBeforeInitialization 方法
            for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
                instance = beanPostProcessor.postProcessBeforeInitialization(beanName, instance);
            }

            //5. 初始化
            if (instance instanceof InitializingBean) {
                ((InitializingBean) instance).afterPropertiesSet();
            }


             // 6. 初始化后
            // 遍历 BeanPostProcessor 列表，执行 postProcessAfterInitialization 方法
            for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
                instance = beanPostProcessor.postProcessAfterInitialization(beanName, instance);
            }


        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }

        return instance;

    }

    public Object getBean(String beanName){
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);

        if (beanDefinition==null){
            throw new NullPointerException();
        }else{
            // 有 Bean 的定义
            // 如果是单例 Bean，则直接从单例池中获取
            if ("singleton".equals(beanDefinition.getScope())) {
                Object bean = singletonObjects.get(beanName);
                // 如果 bean 为空，说明 Bean 还没创建
                if (bean == null) {
                    // 创建的 Bean 对象
                    Object instance = createBean(beanName, beanDefinition);
                    // 放入单例池中
                    singletonObjects.put(beanName, instance);
                }
                // 直接返回单例池中的对象
                return bean;
            } else {
                // 如果是多例 Bean，则每一次 getBean 都创建新的 Bean 对象
                return createBean(beanName, beanDefinition);
            }
        }
    }
}
